﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Static;
using XML;

namespace HashCode
{
	/// <summary>
	/// Класс, содержащий методы для работы с контрольными суммами файлов.
	/// </summary>
	public class ReceptionInformationAboutFolder
	{
		/// <summary>
		/// Путь к исходной папке.
		/// </summary>
		public string Source { get; set; }

		/// <summary>
		/// Список файлов в исходной папке.
		/// </summary>
		public List<InformationAboutFile> AboutFileInSourceDir { get; private set; }

		/// <summary>
		/// Список файлов для копирования.
		/// </summary>
		public List<InformationAboutFile> AboutFileForCopy { get; private set; }

		/// <summary>
		/// Размер папки.
		/// </summary>
		public long DirSize
		{
			get
			{
				DirectoryInfo d = new DirectoryInfo(this.Source);

				////Елси папка не доступна-возвращаем 0.
				if (!d.Exists)
				{
					return 0;
				}

				long size = 0;

				FileInfo[] files = d.GetFiles(".", SearchOption.AllDirectories);
				foreach (FileInfo file in files)
				{
					size += file.Length;
				}

				return size;
			}
		}

		/// <summary>
		/// Количество файлов в папке.
		/// </summary>
		public long NumberOfFiles
		{
			get
			{
				return sourceDirInfo.GetFiles(".", SearchOption.AllDirectories).Count();
			}
		}

		/// <summary>
		/// Атрибут файлов в папке.
		/// </summary>
		public FileAttributes AttributeFolderAndFilesIn
		{
			get
			{
				return this.AttributeFolderAndFilesIn;
			}

			set
			{
				this.AttributeFolderAndFilesIn = value;
				this.sourceDirInfo.Attributes = this.AttributeFolderAndFilesIn;
				if (this.sourceDirInfo != null)
				{
					FileInfo[] files = this.sourceDirInfo.GetFiles(".", SearchOption.AllDirectories);
					foreach (FileInfo file in files)
					{
						file.Attributes = this.AttributeFolderAndFilesIn;
					}
				}
			}
		}

		private DirectoryInfo sourceDirInfo;

		/// <summary>
		/// Конструктор класса.
		/// </summary>
		/// <param name="source">Путь к исходной папке.</param>
		public ReceptionInformationAboutFolder(string source)
		{
			this.Source = source;
			this.sourceDirInfo = System.IO.Directory.Exists(source) ? new DirectoryInfo(source) : null;

			this.AboutFileInSourceDir = UpdateList();
		}

		/// <summary>
		/// Обновление списка файлов в исходной папке.
		/// </summary>
		/// <returns>Список файлов.</returns>
		public List<InformationAboutFile> UpdateList()
		{
			return AddFileInList(this.Source);
		}

		/// <summary>
		/// Метод возвращающий строку пути к файлу лога копирования.
		/// </summary>
		/// <param name="target">Путь к конечной папке.</param>
		/// <param name="newPathForFile">Желаемый путь к файлу.</param>
		/// <returns>Путь к файлу xml, если произошла ошибка при записи в файл-вернет пустую строку.</returns>
		public string GetXmlOfListOfFileForCopy(string target, string newPathForFile)
		{
			if (this.Source == target)
			{
				throw new ApplicationException("Пути совпадают.");
			}

			if (!System.IO.Directory.Exists(this.Source))
			{
				throw new DirectoryNotFoundException("Не задан путь к исходной папке.");
			}

			this.AboutFileForCopy = GetListForCopy(target);

			return CreateAndWriteToXmlFile(newPathForFile, this.AboutFileForCopy, "CopyLog");
		}

		/// <summary>
		/// Создает XML файл содержащий информацию о файлах в папке.
		/// </summary>
		/// <param name="newPath">Путь к месту, где будет сохранен файл.</param>
		/// <returns>Путь к созданному файлу.</returns>
		public string CreateXmlFileOfReleasedFileInfo(string newPath)
		{
			return CreateAndWriteToXmlFile(newPath, this.AboutFileInSourceDir, "ReleasedFilesInfo");
		}

		private string CreateAndWriteToXmlFile(string newPath, List<InformationAboutFile> filesForWrite, string startElement)
		{
			string fileName = string.Format("{0}_{1}_{2}.xml", startElement, DateTime.Now.ToString("yyyyMMddHHmmss"), Guid.NewGuid());

			////Создаем строку пути к файлу xml, с данными об обновляемых файлах.

			string pathToXmlFile = Path.Combine(newPath, fileName);

			if (ForXmlFiles.CreateNewXmlFile(pathToXmlFile, startElement))
			{
				ForXmlFiles.WriteToXmlDocument(pathToXmlFile, filesForWrite, this.Source);
				return pathToXmlFile;
			}

			return string.Empty;
		}

		public List<InformationAboutFile> GetListForCopy(string target)
		{
			List<InformationAboutFile> fileInSourceDir = this.AboutFileInSourceDir;
			////Создаем список из объектов типа InformationAboutFile.
			try
			{
				////Если существует конечная папка, то производим подсчет контрольных сумм, и их сравнение.
				////Если Контрольная сумма файла из конечной папки и файла из исходной совпадают, то удаляем этот файл из списка для копирования.
				if (System.IO.Directory.Exists(target))
				{
					List<InformationAboutFile> aboutFileInTargetDir = AddFileInList(target);
					foreach (InformationAboutFile fileInTarget in aboutFileInTargetDir)
					{
						InformationAboutFile fileForRemove = fileInSourceDir.Find(p => p.Hash == fileInTarget.Hash);
						if (fileForRemove != null)
						{
							fileInSourceDir.Remove(fileForRemove);
						}
					}
				}
			}
			catch (Exception ex)
			{
				throw new ApplicationException(ex.Message);
			}

			return fileInSourceDir;
		}

		private List<InformationAboutFile> AddFileInList(string dir)
		{
			if (System.IO.Directory.Exists(dir))
			{
				FileInfo[] arrayOfFileInDir = new DirectoryInfo(dir).GetFiles(".", SearchOption.AllDirectories);
				////Тоже самое и для файлов в конечной папке.
				return arrayOfFileInDir.Select(file => new InformationAboutFile(file, StaticMetods.ComputeMd5Checksum(file.FullName))).ToList();
			}
			else
			{
				return new List<InformationAboutFile>();
			}
		}
	}
}
